/** @file renderer.cpp
    @brief OpenGL renderer */

#include "render/renderer.h"

//==============================================================================
// Constructors and a destructor
//==============================================================================
G3::RENDERER::RENDERER () {
    MaxWidth = 480;
    MaxHeight = 640;

    Input = NULL;
}

G3::RENDERER::RENDERER (int W, int H) {
    MaxWidth = W;
    MaxHeight = H;

    Input = NULL;

	Camera.SetAspectRatio ((uint) W, (uint) H);
}

G3::RENDERER::RENDERER (int W, int H, float FOV, float Near, float Far) {
    MaxWidth = W;
    MaxHeight = H;

    Input = NULL;

	Camera.SetNearFarDistance (Near, Far);
	Camera.SetFieldOfView (FOV);
	Camera.SetAspectRatio ((uint) W, (uint) H);
}

G3::RENDERER::~RENDERER () {

}

//==============================================================================
// Initializes OpenGL with default parameters, screen Width and Height
//==============================================================================
bool G3::RENDERER::Create (int Width, int Height) {
     MaxWidth = Width;
     MaxHeight = Height;

     Camera.SetAspectRatio ((uint) Width, (uint) Height);

     //Set up a perspective view by default
     SetPerspective (Width, Height);

     glClearColor (0.0f, 0.0f, 0.0f, 0.0f);
     glClearDepth (1.0);
     glDepthFunc (GL_LESS);
     glEnable (GL_DEPTH_TEST);
     glShadeModel (GL_SMOOTH);

     glEnable (GL_TEXTURE_2D);

     return true;
}

//==============================================================================
// Initializes OpenGL with default parameters
//==============================================================================
bool G3::RENDERER::Create () {
    return Create (MaxWidth, MaxHeight);
}

//==============================================================================
// Sets a perspective from viewport coordinates
//==============================================================================
void G3::RENDERER::SetPerspective (int x1, int y1, int x2, int y2) {
	glSetPerspective (RECTI (x1, y1, x2, y2), &Camera.ViewFrustum);
}

//==============================================================================
// Sets a perspective from screen Width and Height
//==============================================================================
void G3::RENDERER::SetPerspective (int Width, int Height) {
	glSetPerspective (RECTI (0, 0, Width, Height), &Camera.ViewFrustum);
}

//==============================================================================
// Sets a perspective from maximum screen Width and Height
//==============================================================================
void G3::RENDERER::SetPerspective () {
	glSetPerspective (RECTI (0, 0, MaxWidth, MaxHeight), &Camera.ViewFrustum);
}

void G3::RENDERER::SetFieldOfView (float FOV) {
	Camera.SetFieldOfView (FOV);
}

//==============================================================================
// Clears buffers
//==============================================================================
void G3::RENDERER::ClearBuffer () {
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glLoadIdentity ();
}

//==============================================================================
// Swaps buffers for DoubleBuffering
//==============================================================================
void G3::RENDERER::SwapBuffer () {
    SDL_GL_SwapBuffers ();
}

//==============================================================================
// Looks around with mouse
//==============================================================================
void G3::RENDERER::Look () {
    Input->Mouse.MouseLook (&Camera);
    Camera.glLook ();
}

//==============================================================================
// Renders a testgrid
//==============================================================================
void G3::RENDERER::RenderGrid (double GridSpacing, COL3F GridColor, bool RenderAxis) {
    float i = 0.0f;
    bool Lighting = false;
    bool Texture = false;
	bool DepthCheck = false;

    glPushMatrix ();
    if (glIsEnabled (GL_TEXTURE_2D)){ glDisable (GL_TEXTURE_2D); Texture = true; }
    if (glIsEnabled (GL_LIGHTING)){ glDisable (GL_LIGHTING); Lighting = true; }

    glColor3f (GridColor.r, GridColor.g, GridColor.b);

    glBegin (GL_LINES);
        // A 100x100 grid, with the cell size of ssize
        for (i=-GridSpacing * 50; i<=GridSpacing * 50; i += GridSpacing) {
            // Vertical lines
            glVertex3f (i, 0.0f, -GridSpacing * 50);
            glVertex3f (i, 0.0f, GridSpacing * 50);
            // Horizontal lines
            glVertex3f (-GridSpacing * 50, 0.0f, i);
            glVertex3f (GridSpacing * 50, 0.0f, i);
        }
    glEnd ();

	if (glIsEnabled (GL_DEPTH_TEST)){ glDisable (GL_DEPTH_TEST); DepthCheck = true; }

    if (RenderAxis) {
		// Valgrind: Invalid write of size 4
        glEnable (GL_LINE_SMOOTH);
        glLineWidth (2.0f);

        glBegin (GL_LINES);
        //X:R
        glColor3f (0.5f, 0, 0);
        glVertex3f (0, 0, 0);
        glVertex3f (GridSpacing * 50, 0, 0);
        // Arrow pointer
        glVertex3f (GridSpacing * 50, 0, 0);
        glVertex3f (GridSpacing * 49, GridSpacing, 0);
        glVertex3f (GridSpacing * 49, GridSpacing, 0);
        glVertex3f (GridSpacing * 49, -GridSpacing, 0);
        glVertex3f (GridSpacing * 49, -GridSpacing, 0);
        glVertex3f (GridSpacing * 50, 0, 0);

        //Y:G
        glColor3f (0, 0.5f, 0);
        glVertex3f (0, 0, 0);
        glVertex3f (0, GridSpacing * 50, 0);
        // Arrow pointer
        glVertex3f (0, GridSpacing * 50, 0);
        glVertex3f (0, GridSpacing * 49, GridSpacing);
        glVertex3f (0, GridSpacing * 49, GridSpacing);
        glVertex3f (0, GridSpacing * 49, -GridSpacing);
        glVertex3f (0, GridSpacing * 49, -GridSpacing);
        glVertex3f (0, GridSpacing * 50, 0);

        //Z:B
        glColor3f (0, 0, 0.5f);
        glVertex3f (0, 0, 0);
        glVertex3f (0, 0, GridSpacing * 50);
        // Arrow pointer
        glVertex3f (0, 0, GridSpacing * 50);
        glVertex3f (0, GridSpacing, GridSpacing * 49);
        glVertex3f (0, GridSpacing, GridSpacing * 49);
        glVertex3f (0, -GridSpacing, GridSpacing * 49);
        glVertex3f (0, -GridSpacing, GridSpacing * 49);
        glVertex3f (0, 0, GridSpacing * 50);
        glEnd ();

        glLineWidth (1.0f);
        glDisable (GL_LINE_SMOOTH);
    }

    glColor3f (1, 1, 1);

	if (DepthCheck) { glEnable (GL_DEPTH_TEST); }

    if (Texture) { glEnable (GL_TEXTURE_2D); }
    if (Lighting) { glEnable (GL_LIGHTING); }
    glPopMatrix ();
}

